

#include "stdafx.h"
#include "pf_circle.h"

//#include <values.h>

#include "pf_constructionline.h"
#include "pf_information.h"
#include "pf_graphicview.h"
#include "pf_painter.h"



/**
 * Default constructor.
 */
PF_Circle::PF_Circle(PF_Container* parent,
                     const PF_CircleAttribute& d)
        :PF_AtomicEntity(parent), data(d) {
    calculateBorders();
}



void PF_Circle::calculateBorders() {
    PF_Vector r(data.radius,data.radius,0.0);
    minV = data.center - r;
    maxV = data.center + r;
}



/**
 * @return Angle length in rad.
 */
double PF_Circle::getAngleLength() const {
    return 2*M_PI;
}



/**
 * @return Length of the circle which is the circumference.
 */
double PF_Circle::getLength() {
	return 2*M_PI*data.radius;
}


/**
 * Creates this circle from a center point and a radius.
 *
 * @param c Center.
 * @param r Radius
 */
bool PF_Circle::createFromCR(const PF_Vector& c, double r) {
    if (fabs(r)>PF_TOLERANCE) {
        data.radius = fabs(r);
        data.center = c;
        return true;
    } else {
        PF_DEBUG->print(PF_Debug::D_WARNING, "PF_Circle::createFromCR(): "
                        "Cannot create a circle with radius 0.0.");
        return false;
    }
}



/**
 * Creates this circle from two opposite points.
 *
 * @param p1 1st point.
 * @param p2 2nd point.
 */
bool PF_Circle::createFrom2P(const PF_Vector& p1, const PF_Vector& p2) {
    if (p1.distanceTo(p2)>PF_TOLERANCE) {
        data.radius = p1.distanceTo(p2)/2.0;
        data.center = p1 + (p2-p1)/2.0;
        return true;
    } else {
        PF_DEBUG->print(PF_Debug::D_WARNING, "PF_Circle::createFrom2P(): "
                        "Cannot create a circle with radius 0.0.");
        return false;
    }
}



/**
 * Creates this circle from 3 given points which define the circle line.
 *
 * @param p1 1st point.
 * @param p2 2nd point.
 * @param p3 3rd point.
 */
bool PF_Circle::createFrom3P(const PF_Vector& p1, const PF_Vector& p2,
                             const PF_Vector& p3) {
    if (p1.distanceTo(p2)>PF_TOLERANCE &&
            p2.distanceTo(p3)>PF_TOLERANCE &&
            p3.distanceTo(p1)>PF_TOLERANCE) {

        // middle points between 3 points:
        PF_Vector mp1, mp2;
        PF_Vector dir1, dir2;
        double a1, a2;

        // intersection of two middle lines
        mp1 = (p1 + p2)/2.0;
        a1 = p1.angleTo(p2) + M_PI/2.0;
        dir1.setPolar(100.0, a1);
        mp2 = (p2 + p3)/2.0;
        a2 = p2.angleTo(p3) + M_PI/2.0;
        dir2.setPolar(100.0, a2);

        PF_ConstructionLineAttribute d1(mp1, mp1 + dir1);
        PF_ConstructionLineAttribute d2(mp2, mp2 + dir2);
        PF_ConstructionLine midLine1(NULL, d1);
        PF_ConstructionLine midLine2(NULL, d2);

        PF_VectorSolutions sol =
            PF_Information::getIntersection(&midLine1, &midLine2);

        data.center = sol.get(0);
        data.radius = data.center.distanceTo(p3);

        if (sol.get(0).valid && data.radius<1.0e14 && data.radius>PF_TOLERANCE) {
            return true;
        } else {
            PF_DEBUG->print(PF_Debug::D_WARNING, "PF_Circle::createFrom3P(): "
                            "Cannot create a circle with inf radius.");
            return false;
        }
    } else {
        PF_DEBUG->print(PF_Debug::D_WARNING, "PF_Circle::createFrom3P(): "
                        "Cannot create a circle with radius 0.0.");
        return false;
    }
}



PF_VectorSolutions PF_Circle::getRefPoints() {
	PF_Vector v1(data.radius, 0.0);
	PF_Vector v2(0.0, data.radius);
	
	PF_VectorSolutions ret(data.center, 
		data.center+v1, data.center+v2,
		data.center-v1, data.center-v2);
	return ret;
}


/**
 * @return Always an invalid vector.
 */
PF_Vector PF_Circle::getNearestEndpoint(const PF_Vector& /*coord*/, double* dist) {
    if (dist!=NULL) {
        *dist = LX_DOUBLEMAX;
    }
    return PF_Vector(false);
}



PF_Vector PF_Circle::getNearestPointOnEntity(const PF_Vector& coord,
        bool /*onEntity*/, double* dist, PF_Entity** entity) {

    PF_Vector vec(false);
    if (entity!=NULL) {
        *entity = this;
    }

    double angle = (coord-data.center).angle();
    vec.setPolar(data.radius, angle);
    vec+=data.center;

    if (dist!=NULL) {
        *dist = fabs((vec-data.center).magnitude()-data.radius);
    }

    return vec;
}



PF_Vector PF_Circle::getNearestCenter(const PF_Vector& coord,
                                      double* dist) {
    if (dist!=NULL) {
        *dist = coord.distanceTo(data.center);
    }
    return data.center;
}



PF_Vector PF_Circle::getNearestMiddle(const PF_Vector& /*coord*/,
                                      double* dist) {
    if (dist!=NULL) {
        *dist = LX_DOUBLEMAX;
    }
    return PF_Vector(false);
}



PF_Vector PF_Circle::getNearestDist(double /*distance*/,
                                    const PF_Vector& /*coord*/,
                                    double* dist) {

    if (dist!=NULL) {
        *dist = LX_DOUBLEMAX;
    }
    return PF_Vector(false);
}

PF_Vector PF_Circle::getNearestDist(double /*distance*/,
                                  bool /*startp*/) {

    return PF_Vector(false);
}



double PF_Circle::getDistanceToPoint(const PF_Vector& coord,
                                     PF_Entity** entity,
                                     LX_Define::ResolveLevel, double) {
    if (entity!=NULL) {
        *entity = this;
    }

    //   Jan 6 2012, allow selections to mid point of circle
    float dToEdge=fabs((coord-data.center).magnitude() - data.radius);
    float dToCenter=data.center.distanceTo(coord);

    if (dToEdge<dToCenter) {
        return dToEdge;
    } else {
        return dToCenter;
    }
}



void PF_Circle::move(PF_Vector offset) {
    data.center.move(offset);
    calculateBorders();
}



void PF_Circle::rotate(PF_Vector center, double angle) {
    data.center.rotate(center, angle);
    calculateBorders();
}



void PF_Circle::scale(PF_Vector center, PF_Vector factor) {
    data.center.scale(center, factor);
    data.radius *= factor.x;
    calculateBorders();
}



void PF_Circle::mirror(PF_Vector axisPoint1, PF_Vector axisPoint2) {
	data.center.mirror(axisPoint1, axisPoint2);
    calculateBorders();
}


void PF_Circle::draw(PF_Painter* painter, PF_GraphicView* view, 
	double /*patternOffset*/) {

    if (painter==NULL || view==NULL) {
        return;
    }


    // simple style-less lines
    if (getPen().getLineType()==LX_Define::SolidLine ||
            isSelected() ||
            view->getDrawingMode()==LX_Define::ModePreview) {

        painter->drawArc(view->toGui(getCenter()),
                         getRadius() * view->getFactor().x,
                         0.0, 2*M_PI,
                         false);
    } else {
		double styleFactor = getStyleFactor(view);
		if (styleFactor<0.0) {
        	painter->drawArc(view->toGui(getCenter()),
                         getRadius() * view->getFactor().x,
                         0.0, 2*M_PI,
                         false);
			return;
		}
		
        // Pattern:
        PF_LinePattern* pat;
        if (isSelected()) {
            pat = &patternSelected;
        } else {
            pat = view->getPattern(getPen().getLineType());
        }

        if (pat==NULL) {
            return;
        }

		if (getRadius()<1.0e-6) {
			return;
		}

        // Pen to draw pattern is always solid:
        PF_Pen pen = painter->getPen();
        pen.setLineType(LX_Define::SolidLine);
        painter->setPen(pen);

        double* da;     // array of distances in x.
        int i;          // index counter

        double length = getAngleLength();

        // create pattern:
        da = new double[pat->num];

        for (i=0; i<pat->num; ++i) {
            da[i] = fabs(pat->pattern[i] * styleFactor)/getRadius();
        }

        double tot=0.0;
        i=0;
        bool done = false;
        double curA = 0.0;
        //double cx = getCenter().x * factor.x + offsetX;
        //double cy = - a->getCenter().y * factor.y + getHeight() - offsetY;
        PF_Vector cp = view->toGui(getCenter());
        double r = getRadius() * view->getFactor().x;

        do {
            if (pat->pattern[i] * styleFactor > 0.0) {
                if (tot+fabs(da[i])<length) {
                    painter->drawArc(cp, r,
                                     curA,
                                     curA + da[i],
                                     false);
                } else {
                    painter->drawArc(cp, r,
                                     curA,
                                     2*M_PI,
                                     false);
                }
            }
            curA+=da[i];
            tot+=fabs(da[i]);
            done=tot>length;

            i++;
            if (i>=pat->num) {
                i=0;
            }
        } while(!done);

        delete[] da;
    }
}



void PF_Circle::moveRef(const PF_Vector& ref, const PF_Vector& offset) {
	PF_Vector v1(data.radius, 0.0);
	PF_Vector v2(0.0, data.radius);
	
    if (ref.distanceTo(data.center + v1)<1.0e-4) {
		data.radius = data.center.distanceTo(data.center + v1 + offset);
    } else if (ref.distanceTo(data.center + v2)<1.0e-4) {
		data.radius = data.center.distanceTo(data.center + v2 + offset);
    } else if (ref.distanceTo(data.center - v1)<1.0e-4) {
		data.radius = data.center.distanceTo(data.center - v1 + offset);
    } else if (ref.distanceTo(data.center - v2)<1.0e-4) {
		data.radius = data.center.distanceTo(data.center - v2 + offset);
	} 
}


/**
 * Dumps the circle's data to stdout.
 */
std::ostream& operator << (std::ostream& os, const PF_Circle& a) {
    os << " Circle: " << a.data << "\n";
    return os;
}

